// Graph edge implementation
use codegraph_core::{CodeEdge, EdgeId, NodeId, EdgeType, EdgeMetadata, Result};
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use std::hash::{Hash, Hasher};

/// Enhanced graph edge with rich metadata and relationship information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphEdge {
    /// Unique identifier for the edge
    id: EdgeId,
    /// Source node ID
    source: NodeId,
    /// Target node ID
    target: NodeId,
    /// Type of relationship
    edge_type: EdgeType,
    /// Rich metadata about the edge
    metadata: EdgeMetadata,
    /// Edge-specific attributes
    attributes: HashMap<String, String>,
    /// Relationship strength/confidence
    strength: f64,
    /// Contextual information about the relationship
    context: RelationshipContext,
    /// Creation and modification timestamps
    timestamps: EdgeTimestamps,
}

/// Contextual information about the relationship
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RelationshipContext {
    /// Source location where the relationship is established
    pub source_location: Option<codegraph_core::SourceLocation>,
    /// Additional context information
    pub context_info: Option<String>,
    /// Evidence supporting this relationship
    pub evidence: Vec<Evidence>,
    /// Confidence score (0.0 to 1.0)
    pub confidence: f64,
}

/// Evidence supporting a relationship
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Evidence {
    /// Type of evidence
    pub evidence_type: EvidenceType,
    /// Description of the evidence
    pub description: String,
    /// Location where evidence was found
    pub location: Option<codegraph_core::SourceLocation>,
    /// Weight of this evidence
    pub weight: f64,
}

/// Types of evidence for relationships
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EvidenceType {
    /// Syntactic evidence (direct code reference)
    Syntactic,
    /// Semantic evidence (inferred relationship)
    Semantic,
    /// Structural evidence (code organization)
    Structural,
    /// Behavioral evidence (runtime behavior)
    Behavioral,
    /// Documentation evidence (comments, docs)
    Documentation,
}

/// Timestamps for tracking edge lifecycle
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EdgeTimestamps {
    /// When the edge was created
    pub created_at: chrono::DateTime<chrono::Utc>,
    /// When the edge was last modified
    pub modified_at: chrono::DateTime<chrono::Utc>,
    /// When the edge was last accessed
    pub accessed_at: chrono::DateTime<chrono::Utc>,
}

impl Default for RelationshipContext {
    fn default() -> Self {
        Self {
            source_location: None,
            context_info: None,
            evidence: Vec::new(),
            confidence: 1.0,
        }
    }
}

impl GraphEdge {
    /// Create a new graph edge
    pub fn new(
        id: EdgeId,
        source: NodeId,
        target: NodeId,
        edge_type: EdgeType,
    ) -> Self {
        let now = chrono::Utc::now();
        Self {
            id,
            source,
            target,
            edge_type,
            metadata: EdgeMetadata::default(),
            attributes: HashMap::new(),
            strength: 1.0,
            context: RelationshipContext::default(),
            timestamps: EdgeTimestamps {
                created_at: now,
                modified_at: now,
                accessed_at: now,
            },
        }
    }

    /// Create a new edge with context
    pub fn with_context(
        id: EdgeId,
        source: NodeId,
        target: NodeId,
        edge_type: EdgeType,
        context: RelationshipContext,
    ) -> Self {
        let mut edge = Self::new(id, source, target, edge_type);
        edge.context = context;
        edge
    }

    /// Create a new edge with strength
    pub fn with_strength(
        id: EdgeId,
        source: NodeId,
        target: NodeId,
        edge_type: EdgeType,
        strength: f64,
    ) -> Self {
        let mut edge = Self::new(id, source, target, edge_type);
        edge.strength = strength.clamp(0.0, 1.0);
        edge
    }

    /// Get relationship strength
    pub fn strength(&self) -> f64 {
        self.strength
    }

    /// Set relationship strength
    pub fn set_strength(&mut self, strength: f64) {
        self.strength = strength.clamp(0.0, 1.0);
        self.timestamps.modified_at = chrono::Utc::now();
    }

    /// Get relationship context
    pub fn context(&self) -> &RelationshipContext {
        &self.context
    }

    /// Update relationship context
    pub fn update_context(&mut self, context: RelationshipContext) {
        self.context = context;
        self.timestamps.modified_at = chrono::Utc::now();
    }

    /// Add evidence to the relationship
    pub fn add_evidence(&mut self, evidence: Evidence) {
        self.context.evidence.push(evidence);
        self.timestamps.modified_at = chrono::Utc::now();
    }

    /// Get edge attributes
    pub fn attributes(&self) -> &HashMap<String, String> {
        &self.attributes
    }

    /// Set an attribute
    pub fn set_attribute(&mut self, key: String, value: String) {
        self.attributes.insert(key, value);
        self.timestamps.modified_at = chrono::Utc::now();
    }

    /// Get an attribute
    pub fn get_attribute(&self, key: &str) -> Option<&String> {
        self.attributes.get(key)
    }

    /// Remove an attribute
    pub fn remove_attribute(&mut self, key: &str) -> Option<String> {
        let result = self.attributes.remove(key);
        if result.is_some() {
            self.timestamps.modified_at = chrono::Utc::now();
        }
        result
    }

    /// Update access timestamp
    pub fn mark_accessed(&mut self) {
        self.timestamps.accessed_at = chrono::Utc::now();
    }

    /// Get timestamps
    pub fn timestamps(&self) -> &EdgeTimestamps {
        &self.timestamps
    }

    /// Check if this edge represents a strong relationship
    pub fn is_strong(&self) -> bool {
        self.strength >= 0.7
    }

    /// Check if this edge has high confidence
    pub fn is_confident(&self) -> bool {
        self.context.confidence >= 0.8
    }

    /// Get the reverse edge type (if applicable)
    pub fn reverse_edge_type(&self) -> Option<EdgeType> {
        match self.edge_type {
            EdgeType::Calls => Some(EdgeType::References), // Called by
            EdgeType::Inherits => None, // Inheritance is directional
            EdgeType::Implements => None, // Implementation is directional
            EdgeType::Imports => None, // Import is directional
            EdgeType::References => Some(EdgeType::References), // Bidirectional
            EdgeType::Defines => Some(EdgeType::References), // Defined by
            EdgeType::Contains => None, // Containment is directional
            EdgeType::DependsOn => None, // Dependency is directional
            EdgeType::Uses => Some(EdgeType::References), // Used by
            EdgeType::ControlFlow => None, // Control flow is directional
            EdgeType::DataFlow => None, // Data flow is directional
            EdgeType::TypeRelation => Some(EdgeType::TypeRelation), // Can be bidirectional
            EdgeType::Unknown => None,
        }
    }
}

impl CodeEdge for GraphEdge {
    fn id(&self) -> EdgeId {
        self.id
    }

    fn source(&self) -> NodeId {
        self.source
    }

    fn target(&self) -> NodeId {
        self.target
    }

    fn edge_type(&self) -> &EdgeType {
        &self.edge_type
    }

    fn metadata(&self) -> &EdgeMetadata {
        &self.metadata
    }

    fn update_metadata(&mut self, metadata: EdgeMetadata) -> Result<()> {
        self.metadata = metadata;
        self.timestamps.modified_at = chrono::Utc::now();
        Ok(())
    }
}

impl PartialEq for GraphEdge {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for GraphEdge {}

impl Hash for GraphEdge {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.id.hash(state);
    }
}

impl std::fmt::Display for GraphEdge {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{} --[{:?}]--> {} (strength: {:.2}, confidence: {:.2})",
            self.source,
            self.edge_type,
            self.target,
            self.strength,
            self.context.confidence
        )
    }
}
